﻿using System.Collections.Generic;
using SpiceNet.Circuits;
using SpiceNet.Diagnostics;
using System.Numerics;
using SpiceNet.Parameters;
using SpiceNet.Simulations;

namespace SpiceNet.Models
{
    /// <summary>
    /// A voltage controlled current source
    /// </summary>
    public class VoltageControlledCurrentSource : Instance
    {
        /*
         * NOTES:
         * - In the original Spice code, VCCSstates is never used, so it is removed here
         */

        /// <summary>
        /// Get parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        #region Parameters
        private enum _c
        {
            /* device parameters */
            VCCS_TRANS = 1,
            VCCS_IC = 2,
            VCCS_POS_NODE = 3,
            VCCS_NEG_NODE = 4,
            VCCS_CONT_P_NODE = 5,
            VCCS_CONT_N_NODE = 6,
            VCCS_CONT_V_OLD = 7,
            VCCS_TRANS_SENS = 8,
            VCCS_CURRENT = 9,
            VCCS_POWER = 10,
            VCCS_VOLTS = 11,

            /* device questions */
            VCCS_QUEST_SENS_REAL = 201,
            VCCS_QUEST_SENS_IMAG = 202,
            VCCS_QUEST_SENS_MAG = 203,
            VCCS_QUEST_SENS_PH = 204,
            VCCS_QUEST_SENS_CPLX = 205,
            VCCS_QUEST_SENS_DC = 206
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "gain", new IP(IF.IOPU, (int)_c.VCCS_TRANS, T.REAL, "Transconductance of source (gain)") },
            { "sens_trans", new IP(IF.IP, (int)_c.VCCS_TRANS_SENS, T.FLAG, "flag to request sensitivity WRT transconductance") },
            { "pos_node", new IP(IF.OPU, (int)_c.VCCS_POS_NODE, T.INTEGER, "Positive node of source") },
            { "neg_node", new IP(IF.OPU, (int)_c.VCCS_NEG_NODE, T.INTEGER, "Negative node of source") },
            { "cont_p_node", new IP(IF.OPU, (int)_c.VCCS_CONT_P_NODE, T.INTEGER, "Positive node of contr. source") },
            { "cont_n_node", new IP(IF.OPU, (int)_c.VCCS_CONT_N_NODE, T.INTEGER, "Negative node of contr. source") },
            { "ic", new IP(IF.IP, (int)_c.VCCS_IC, T.REAL, "Initial condition of controlling source") },
            { "i", new IP(IF.OP, (int)_c.VCCS_CURRENT, T.REAL, "Output current") },
            { "v", new IP(IF.OP, (int)_c.VCCS_VOLTS, T.REAL, "Voltage across output") },
            { "p", new IP(IF.OP, (int)_c.VCCS_POWER, T.REAL, "Power") },
            { "sens_dc", new IP(IF.OPU, (int)_c.VCCS_QUEST_SENS_DC, T.REAL, "dc sensitivity ") },
            { "sens_real", new IP(IF.OPU, (int)_c.VCCS_QUEST_SENS_REAL, T.REAL, "real part of ac sensitivity") },
            { "sens_imag", new IP(IF.OPU, (int)_c.VCCS_QUEST_SENS_IMAG, T.REAL, "imag part of ac sensitivity") },
            { "sens_mag", new IP(IF.OPU, (int)_c.VCCS_QUEST_SENS_MAG, T.REAL, "sensitivity of ac magnitude") },
            { "sens_ph", new IP(IF.OPU, (int)_c.VCCS_QUEST_SENS_PH, T.REAL, "sensitivity of ac phase") },
            { "sens_cplx", new IP(IF.OPU, (int)_c.VCCS_QUEST_SENS_CPLX, T.COMPLEX, "ac sensitivity") }
        };

        public Parameter<double> VCCScoeff { get; } = new Parameter<double>();
        public int VCCSsenParmNo { get; set; }
        #endregion

        /// <summary>
        /// Private variables
        /// </summary>
        private int VCCSposNode;
        private int VCCSnegNode;
        private int VCCScontPosNode;
        private int VCCScontNegNode;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        public VoltageControlledCurrentSource(string name) : base(name, 4, typeof(VoltageControlledCurrentSourceModel)) { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the Voltage-Controlled Current source</param>
        /// <param name="nplus">The name of the positive output node</param>
        /// <param name="nminus">The name of the negative output node</param>
        /// <param name="ncplus">The name of the positive input node</param>
        /// <param name="ncminus">The name of the negative input node</param>
        /// <param name="gain">The transconductance gain (S - Siemens/Mho)</param>
        public VoltageControlledCurrentSource(string name, string nplus, string nminus, string ncplus, string ncminus, double gain) : this(name)
        {
            Connect(nplus, nminus, ncplus, ncminus);
            VCCScoeff.Par(gain);
        }

        /// <summary>
        /// Set parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.VCCS_TRANS:
                    VCCScoeff.Par((double)value);
                    break;
                case _c.VCCS_TRANS_SENS:
                    VCCSsenParmNo = (int)value;
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Get parameter
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.VCCS_TRANS:
                    return VCCScoeff.Value;
                case _c.VCCS_POS_NODE:
                    return VCCSposNode;
                case _c.VCCS_NEG_NODE:
                    return VCCSnegNode;
                case _c.VCCS_CONT_P_NODE:
                    return VCCScontPosNode;
                case _c.VCCS_CONT_N_NODE:
                    return VCCScontNegNode;
                case _c.VCCS_CONT_V_OLD:
                    // return ckt.State.States[0][VCCSstate + VCCScontVOld];
                    return null;
                case _c.VCCS_CURRENT:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else
                    {
                        return (ckt.State.Solution[VCCScontPosNode]
                                - ckt.State.Solution[VCCScontNegNode]) *
                                (VCCScoeff);
                    }
                case _c.VCCS_VOLTS:
                    return (ckt.State.Solution[VCCSposNode]
                    - ckt.State.Solution[VCCSnegNode]);
                case _c.VCCS_POWER:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else
                    {
                        return (ckt.State.Solution[VCCScontPosNode]
                                - ckt.State.Solution[VCCScontNegNode]) *
                                (VCCScoeff) * (ckt.State.Solution[VCCSposNode]
                                - ckt.State.Solution[VCCSnegNode]);
                    }
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Setup the device
        /// </summary>
        /// <param name="ckt"></param>
        public override void Setup(Circuit ckt, Model model, object p = null)
        {
            var nodes = BindNodes(ckt);
            VCCSposNode = nodes[0].Number;
            VCCSnegNode = nodes[1].Number;
            VCCScontPosNode = nodes[2].Number;
            VCCScontNegNode = nodes[3].Number;
        }

        /// <summary>
        /// Temperature dependent stuff
        /// </summary>
        /// <param name="ckt"></param>
        public override void Temperature(Circuit ckt, Model model, object p = null)
        {
        }

        /// <summary>
        /// Load the voltage controlled source in the circuit
        /// </summary>
        /// <param name="ckt"></param>
        public override void Load(Circuit ckt, Model model, object p = null)
        {
            CircuitState state = ckt.State;
            state.Matrix[VCCSposNode, VCCScontPosNode] += VCCScoeff;
            state.Matrix[VCCSposNode, VCCScontNegNode] -= VCCScoeff;
            state.Matrix[VCCSnegNode, VCCScontPosNode] -= VCCScoeff;
            state.Matrix[VCCSnegNode, VCCScontNegNode] += VCCScoeff;
        }

        /// <summary>
        /// Load the voltage controlled source in the circuit
        /// </summary>
        /// <param name="ckt"></param>
        public override void AcLoad(Circuit ckt, Model model, object p = null)
        {
            CircuitStateComplex state = ckt.StateComplex;

            state.Matrix[VCCSposNode, VCCScontPosNode] += VCCScoeff.Value;
            state.Matrix[VCCSposNode, VCCScontNegNode] -= VCCScoeff.Value;
            state.Matrix[VCCSnegNode, VCCScontPosNode] -= VCCScoeff.Value;
            state.Matrix[VCCSnegNode, VCCScontNegNode] += VCCScoeff.Value;
        }
    }
}
